﻿using System;
using System.Collections;
using System.Collections.Generic;

using Netbits.Common.Threading;

namespace Netbits.Common.Collections
{
	/// <summary>
	/// This class represent a List that was locked with the lock-class. 
	/// Usage of read/upgrade and write lock. 
	/// </summary>
	/// <typeparam name="TItem">Type of elements in the list</typeparam>
	public class LockedList<TItem> : IList<TItem>, ICollection<TItem>, IEnumerable<TItem>
	{
		#region Private Variables

		/// <summary>
		/// List of all elements
		/// </summary>
		protected readonly List<TItem> _Inner;
		/// <summary>
		/// Lock instance
		/// </summary>
		protected readonly Lock _Lock;

		#endregion

		#region Constructor

		/// <summary>
		/// Create a new Locked-List with new Lock instance.
		/// </summary>
		public LockedList()
			: this( new Lock() )
		{
		}

		/// <summary>
		/// Create a new Locked-List.
		/// You must set a Lock instance for locking the list.
		/// </summary>
		/// <param name="Lock_">Lock instance to use</param>
		public LockedList( Lock Lock_ )
		{
			if( Lock_ == null )
				throw new ArgumentNullException( "Lock_" );

			_Inner = new List<TItem>();
			_Lock = Lock_;
		}

		#endregion

		#region Public Properties

		/// <summary>
		/// Returns the Lock-Object for external locking
		/// </summary>
		/// <value>The used lock-object</value>
		public Lock SyncObject
		{
			get
			{
				return _Lock;
			}
		}

		/// <summary>
		/// Returns the number of elements contained in the list
		/// </summary>
		/// <value>The number of elements actually contained in the list</value>
		public virtual int Count
		{
			get
			{
				try
				{
					_Lock.EnterReadLock();
					return _Inner.Count;
				}
				finally
				{
					_Lock.ExitReadLock();
				}
			}
		}

		/// <summary>
		/// Returns a value indicating whether the list is read-only.
		/// <remarks>
		/// This implementation of list returns always false.
		/// </remarks>
		/// </summary>
		/// <value>true if the list is read-only otherwise false</value>
		public virtual bool IsReadOnly
		{
			get
			{
				return false;
			}
		}

		/// <summary>
		/// Get the item at the specified index or set this.
		/// If the index is out of range the default(TItem) value was returned
		/// </summary>
		/// <param name="Index_">The zero based index.</param>
		public virtual TItem this[int Index_]
		{
			get
			{
				try
				{
					_Lock.EnterReadLock();
					if( Index_ < 0 || Index_ > _Inner.Count - 1 )
						return default( TItem );

					return _Inner[Index_];
				}
				finally
				{
					_Lock.ExitReadLock();
				}
			}
			set
			{
				try
				{
					_Lock.EnterWriteLock();
					if( Index_ < 0 || Index_ >= _Inner.Count - 1 )
						throw new IndexOutOfRangeException();

					_Inner[Index_] = value;
				}
				finally
				{
					_Lock.ExitWriteLock();
				}
			}
		}

		/// <summary>
		/// Returns the first item in the list, otherwise return default(TItem)
		/// </summary>
		/// <value>The first item</value>
		public virtual TItem First
		{
			get
			{
				try
				{
					_Lock.EnterReadLock();
					if( _Inner.Count == 0 )
						return default( TItem );

					return _Inner[0];
				}
				finally
				{
					_Lock.ExitReadLock();
				}
			}
		}

		/// <summary>
		/// Returns the last item in the list, otherwise return default(TItem)
		/// </summary>
		/// <value>The last item</value>
		public virtual TItem Last
		{
			get
			{
				try
				{
					_Lock.EnterReadLock();
					if( _Inner.Count == 0 )
						return default( TItem );

					return _Inner[_Inner.Count - 1];
				}
				finally
				{
					_Lock.ExitReadLock();
				}
			}
		}

		#endregion

		#region Public Functions

		/// <summary>
		/// Add new item to the end of the list. 
		/// </summary>
		/// <param name="Item_">The item to be added to the end of the list</param>
		public virtual void Add( TItem Item_ )
		{
			if( Item_ == null )
				throw new ArgumentNullException( "Item_" );

			try
			{
				_Lock.EnterWriteLock();
				_Inner.Add( Item_ );
			}
			finally
			{
				_Lock.ExitWriteLock();
			}
		}

		/// <summary>
		/// Add the items of the enumerable list to the end of the list
		/// </summary>
		/// <param name="Items_">The items to be added to the end of the list</param>
		public virtual void AddRange( IEnumerable<TItem> Items_ )
		{
			if( Items_ == null )
				throw new ArgumentNullException( "Items_" );

			try
			{
				_Lock.EnterWriteLock();
				_Inner.AddRange( Items_ );
			}
			finally
			{
				_Lock.ExitWriteLock();
			}
		}

		/// <summary>
		/// Removes all items from the list.
		/// </summary>
		public virtual void Clear()
		{
			try
			{
				_Lock.EnterWriteLock();
				_Inner.Clear();
			}
			finally
			{
				_Lock.ExitWriteLock();
			}
		}

		/// <summary>
		/// Determines whether an item is in the list.
		/// </summary>
		/// <param name="Item_">The item to locate in the list</param>
		/// <returns>true it the item was found, otherwise false</returns>
		public virtual bool Contains( TItem Item_ )
		{
			if( Item_ == null )
				throw new ArgumentNullException( "Item_" );

			try
			{
				_Lock.EnterReadLock();
				return _Inner.Contains( Item_ );
			}
			finally
			{
				_Lock.ExitReadLock();
			}
		}

		/// <summary>
		/// Copies all items from the list to the one-dimensional array.
		/// </summary>
		/// <param name="Array_">The one-dimensional array</param>
		/// <param name="ArrayIndex_">The zero based index in the one-dimensional array at witch copying begin</param>
		public virtual void CopyTo( TItem[] Array_, int ArrayIndex_ )
		{
			try
			{
				_Lock.EnterReadLock();
				_Inner.CopyTo( Array_, ArrayIndex_ );
			}
			finally
			{
				_Lock.ExitReadLock();
			}
		}

		/// <summary>
		/// Remove the first occurrence of the specified item from the list.
		/// </summary>
		/// <param name="Item_">The item to remove from the list.</param>
		public virtual bool Remove( TItem Item_ )
		{
			try
			{
				_Lock.EnterWriteLock();
				return _Inner.Remove( Item_ );
			}
			finally
			{
				_Lock.ExitWriteLock();
			}
		}

		/// <summary>
		/// Returns a new locked-enumerator that iterates through the list.
		/// </summary>
		/// <returns>Locked-enumerator for the list</returns>
		public virtual IEnumerator GetEnumerator()
		{
			return new LockedEnumerator<TItem>( this, _Lock );
		}

		/// <summary>
		/// Returns a new typed locked-enumerator that iterates through the list.
		/// </summary>
		/// <returns>Typed locked-enumerator for the list</returns>
		IEnumerator<TItem> IEnumerable<TItem>.GetEnumerator()
		{
			return new LockedEnumerator<TItem>( _Inner, _Lock );
		}

		/// <summary>
		/// Returns the index of the specified item
		/// </summary>
		/// <param name="Item_">The item to locate in the list</param>
		/// <returns>The zero-based index of the first occurrence of the item, otherwise -1</returns>
		public virtual int IndexOf( TItem Item_ )
		{
			try
			{
				_Lock.EnterReadLock();
				return _Inner.IndexOf( Item_ );
			}
			finally
			{
				_Lock.ExitReadLock();
			}
		}

		/// <summary>
		/// Insert the item into the list at the specified index.
		/// </summary>
		/// <param name="Item_">The item to insert.</param>
		/// <param name="Index_">The zero-based index at witch item should be inserted</param>
		public virtual void Insert( int Index_, TItem Item_ )
		{
			try
			{
				_Lock.EnterWriteLock();
				_Inner.Insert( Index_, Item_ );
			}
			finally
			{
				_Lock.ExitWriteLock();
			}
		}

		/// <summary>
		/// Remove the item at the specified index.
		/// </summary>
		/// <param name="Index_">The zero-based index of the item to remove.</param>
		public virtual void RemoveAt( int Index_ )
		{
			try
			{
				_Lock.EnterWriteLock();
				if( Index_ < 0 || Index_ > _Inner.Count - 1 )
					return;

				_Inner.Remove( _Inner[Index_] );
			}
			finally
			{
				_Lock.ExitWriteLock();
			}
		}

		/// <summary>
		/// Remove a range of items from the list.
		/// </summary>
		/// <param name="Index_">The zero-based starting index of the range of items to remove</param>
		/// <param name="Count_">The number of items to remove.</param>
		public virtual void RemoveRange( int Index_, int Count_ )
		{
			try
			{
				_Lock.EnterWriteLock();
				if( Index_ < 0 || Index_ > _Inner.Count - 1 )
					return;

				int count = Count_;
				if( _Inner.Count - Index_ < Count_ )
					count = _Inner.Count - Index_;

				_Inner.RemoveRange( Index_, count );
			}
			finally
			{
				_Lock.ExitWriteLock();
			}
		}

		/// <summary>
		/// Perform the specified action on each item at the list.
		/// </summary>
		/// <param name="Action_">The action delegate to perform on each item of the list</param>
		public void ForEach( Action<TItem> Action_ )
		{
			if( Action_ == null )
				return;

			try
			{
				_Lock.EnterReadLock();
				foreach( TItem item in _Inner )
					Action_( item );
			}
			finally
			{
				_Lock.ExitReadLock();
			}
		}

		/// <summary>
		/// Determines whether the list contains items that match the conditions defined by the specified predicate
		/// </summary>
		/// <param name="Match_">The predicate delegate that defines the conditions of the items to search for</param>
		public bool Exists( Predicate<TItem> Match_ )
		{
			if( Match_ == null )
				return false;

			try
			{
				_Lock.EnterReadLock();

				foreach( TItem item in _Inner )
				{
					if( Match_( item ) )
						return true;
				}
				return false;
			}
			finally
			{
				_Lock.ExitReadLock();
			}
		}

		/// <summary>
		/// Reverses the order of the items in the entire list.
		/// </summary>
		public void Reverse()
		{
			try
			{
				_Lock.EnterWriteLock();
				_Inner.Reverse();
			}
			finally
			{
				_Lock.ExitWriteLock();
			}
		}

		/// <summary>
		/// Sorts the items in the list using the default comparer.
		/// </summary>
		public void Sort()
		{
			try
			{
				_Lock.EnterWriteLock();
				_Inner.Sort();
			}
			finally
			{
				_Lock.ExitWriteLock();
			}
		}

		/// <summary>
		/// Sorts the items in the list using the specified comparer.
		/// </summary>
		/// <param name="Compare_">The IComparer implementation to use when comparing elements.</param>
		public void Sort( IComparer<TItem> Compare_ )
		{
			try
			{
				_Lock.EnterWriteLock();
				_Inner.Sort( Compare_ );
			}
			finally
			{
				_Lock.ExitWriteLock();
			}
		}

		#endregion
	}
}
